package com.sxhuayuan.parking.service.impl;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Repository;

import com.sxhuayuan.parking.service.RedisService;

@Repository("redisServiceImpl")
public class RedisServiceImpl implements RedisService {

	private Logger log = LoggerFactory.getLogger(getClass());

	/**
	 * 前缀
	 */
	public static final String KEY_PREFIX_VALUE = "parking:value:";
	public static final String KEY_PREFIX_SET = "parking:set:";
	public static final String KEY_PREFIX_LIST = "parking:list:";
	public static final String KEY_PREFIX_HASH = "parking:hash:";
	public static final String KEY_PREFIX_GEO = "parking:geo:";

	@Resource(name = "redisTemplate")
	RedisTemplate<String, String> redisTemplate;

	/**
	 * 设置redisTemplate
	 * 
	 * @param redisTemplate
	 *            the redisTemplate to set
	 */
	public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}

	/**
	 * 获取 RedisSerializer <br>
	 * ------------------------------<br>
	 */
	protected RedisSerializer<String> getRedisSerializer() {
		return redisTemplate.getStringSerializer();
	}

	@Override
	public boolean saveString(String k, final String t) {
		final String _k = KEY_PREFIX_VALUE + k;
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] key = serializer.serialize(_k);
				byte[] name = serializer.serialize(t);
				return connection.setNX(key, name);
			}
		});
		return result;
	}

	@Override
	public boolean updateString(String k, String t) {
		final String _k = KEY_PREFIX_VALUE + k;
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] key = serializer.serialize(_k);
				byte[] name = serializer.serialize(t);
				return connection.set(key, name);
			}
		});
		return result;
	}

	@Override
	public boolean updateString(String k, String t, long time) {
		final String _k = KEY_PREFIX_VALUE + k;
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] key = serializer.serialize(_k);
				byte[] name = serializer.serialize(t);
				Boolean rs = connection.set(key, name);
				if (time > 0)
					redisTemplate.expire(_k, time, TimeUnit.SECONDS);
				return rs;
			}
		});
		return result;
	}

	@Override
	public boolean saveString(String k, final String t, final long time) {
		final String _k = KEY_PREFIX_VALUE + k;
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] key = serializer.serialize(_k);
				byte[] name = serializer.serialize(t);
				connection.setNX(key, name);
				if (time > 0)
					redisTemplate.expire(_k, time, TimeUnit.SECONDS);
				return true;
			}
		});
		return result;
	}

	@Override
	public boolean removeString(String k) {
		final String _k = KEY_PREFIX_VALUE + k;
		long result = redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] key = serializer.serialize(_k);
				return connection.del(key);
			}
		});
		return result > 0;
	}

	@Override
	public String getString(String k) {
		final String _k = KEY_PREFIX_VALUE + k;
		String result = redisTemplate.execute(new RedisCallback<String>() {
			public String doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] key = serializer.serialize(_k);
				byte[] bs = connection.get(key);
				return bs == null ? null : new String(bs);
			}
		});
		return result;
	}

	public boolean saveSet(String k, String v, long time) {
		String key = KEY_PREFIX_SET + k;
		try {
			SetOperations<String, String> valueOps = redisTemplate.opsForSet();
			valueOps.add(key, v);
			if (time > 0)
				redisTemplate.expire(key, time, TimeUnit.SECONDS);
		} catch (Throwable t) {
			log.error("缓存[" + key + "]失败, value[" + v + "]", t);
		}
		return true;
	}

	public boolean saveSet(String k, String v) {
		return saveSet(k, v, -1);
	}

	public boolean saveSet(String k, Set<String> v, long time) {
		String key = KEY_PREFIX_SET + k;
		try {
			SetOperations<String, String> setOps = redisTemplate.opsForSet();
			setOps.add(key, v.toArray(new String[v.size()]));
			if (time > 0)
				redisTemplate.expire(key, time, TimeUnit.SECONDS);
			return true;
		} catch (Throwable t) {
			log.error("缓存[" + key + "]失败, value[" + v + "]", t);
		}
		return false;
	}

	public boolean saveSet(String k, Set<String> v) {
		return saveSet(k, v, -1);
	}

	@Override
	public boolean leftpush(String queueName, String v) {
		String key = KEY_PREFIX_SET + queueName;
		try {
			ListOperations<String, String> operation = redisTemplate.opsForList();
			return operation.leftPush(key, v) > 0;
		} catch (Exception e) {
			log.error("leftpush列表失败key[" + key + ", error[" + e + "]");
		}
		return false;
	}

	public boolean rightpush(String queueName, String v) {
		String key = KEY_PREFIX_SET + queueName;
		try {
			ListOperations<String, String> operation = redisTemplate.opsForList();
			return operation.rightPush(key, v) > 0;
		} catch (Exception e) {
			log.error("rightpush列表失败key[" + key + ", error[" + e + "]");
		}
		return false;
	}

	@Override
	public String rightpop(String queueName) {
		String key = KEY_PREFIX_SET + queueName;
		try {
			ListOperations<String, String> operation = redisTemplate.opsForList();
			return operation.rightPop(key);
		} catch (Exception e) {
			log.error("rightpop失败key[" + key + ", error[" + e + "]");
		}
		return null;
	}

	@Override
	public String rightpop(String queueName, long time, TimeUnit unit) {
		String key = KEY_PREFIX_SET + queueName;
		try {
			ListOperations<String, String> operation = redisTemplate.opsForList();
			return operation.rightPop(key, time, unit);
		} catch (Exception e) {
			log.error("rightpop失败key[" + key + ", error[" + e + "]");
		}
		return null;
	}

	public Set<String> getSet(String k) {
		String key = KEY_PREFIX_SET + k;
		try {
			SetOperations<String, String> setOps = redisTemplate.opsForSet();
			return setOps.members(key);
		} catch (Throwable t) {
			log.error("获取set缓存失败key[" + key + ", error[" + t + "]");
		}
		return null;
	}

	public boolean saveList(String k, String v, long time) {
		String key = KEY_PREFIX_LIST + k;
		try {
			ListOperations<String, String> listOps = redisTemplate.opsForList();
			listOps.rightPush(key, v);
			if (time > 0)
				redisTemplate.expire(key, time, TimeUnit.SECONDS);
			return true;
		} catch (Throwable t) {
			log.error("缓存[" + key + "]失败, value[" + v + "]", t);
		}
		return false;
	}

	public boolean saveList(String k, String v) {
		return saveList(k, v, -1);
	}

	public boolean saveList(String k, List<String> v, long time) {
		String key = KEY_PREFIX_LIST + k;
		try {
			ListOperations<String, String> listOps = redisTemplate.opsForList();
			listOps.rightPushAll(key, v);
			if (time > 0)
				redisTemplate.expire(key, time, TimeUnit.SECONDS);
			return true;
		} catch (Throwable t) {
			log.error("缓存[" + key + "]失败, value[" + v + "]", t);
		}
		return false;
	}

	public boolean saveList(String k, List<String> v) {
		return saveList(k, v, -1);
	}

	public List<String> getList(String k, long start, long end) {
		String key = KEY_PREFIX_LIST + k;
		try {
			ListOperations<String, String> listOps = redisTemplate.opsForList();
			return listOps.range(key, start, end);
		} catch (Throwable t) {
			log.error("获取list缓存失败key[" + key + "]" + ", error[" + t + "]");
		}
		return null;
	}

	public long getListSize(String k) {
		String key = KEY_PREFIX_LIST + k;
		try {
			ListOperations<String, String> listOps = redisTemplate.opsForList();
			return listOps.size(key);
		} catch (Throwable t) {
			log.error("获取list长度失败key[" + key + "], error[" + t + "]");
		}
		return 0;
	}

	public boolean removeOneOfList(String k) {
		String key = KEY_PREFIX_LIST + k;
		try {
			ListOperations<String, String> listOps = redisTemplate.opsForList();
			listOps.rightPop(key);
			return true;
		} catch (Throwable t) {
			log.error("移除list缓存失败key[" + key + ", error[" + t + "]");
		}
		return false;
	}

	@Override
	public boolean existsInSet(String k, String v) {
		String key = KEY_PREFIX_SET + k;
		try {
			SetOperations<String, String> setOps = redisTemplate.opsForSet();
			return setOps.isMember(key, v);
		} catch (Throwable t) {
			log.error("获取set[" + key + "]失败, value[" + v + "]", t);
		}
		return false;
	}

	@Override
	public boolean geoAdd(String set, final String jdLongitude, final String wdLatitude, final String member) {
		final String key = KEY_PREFIX_GEO + set;
		log.debug("redis添加地理位置:{},{},{},{}", key, jdLongitude, wdLatitude, member);
		return redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[] _key = serializer.serialize(key);
				Point point = new Point(Double.valueOf(jdLongitude), Double.valueOf(wdLatitude));
				byte[] value = serializer.serialize(member);
				return connection.geoAdd(_key, point, value);
			}
		}) > 0;
	}

	@Override
	public boolean geoRemove(String set, final String... members) {
		final String key = KEY_PREFIX_GEO + set;
		log.debug("redis清空地理位置:{}, {}", key, members);
		return redisTemplate.execute(new RedisCallback<Long>() {
			public Long doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				byte[][] _members = null;
				if (members != null && members.length > 0) {
					_members = new byte[100][members.length];
					for (int i = 0; i < members.length; i++) {
						_members[i] = serializer.serialize(members[i]);
					}
				}
				byte[] _key = serializer.serialize(key);
				return _members == null ? connection.del(_key) : connection.geoRemove(_key, _members);
			}
		}) > 0;
	}

	@Override
	public List<GeoResult<GeoLocation<byte[]>>> findGeoRadiusList(String set, String jdLongitude, String wdLatitude, long m) {
		final String key = KEY_PREFIX_GEO + set;
		Point point = new Point(Double.valueOf(jdLongitude), Double.valueOf(wdLatitude));
		final Circle circle = new Circle(point, m);
		// 这样查不出来.....
		// GeoResults<GeoLocation<String>> geoResults =
		// redisTemplate.opsForGeo().geoRadius(key, within);
		// return geoResults.getContent();

		GeoResults<GeoLocation<byte[]>> geoResults = redisTemplate.execute(new RedisCallback<GeoResults<GeoLocation<byte[]>>>() {

			@Override
			public GeoResults<GeoLocation<byte[]>> doInRedis(RedisConnection connection) throws DataAccessException {
				RedisSerializer<String> serializer = getRedisSerializer();
				// 返回距离
				GeoRadiusCommandArgs args = GeoRadiusCommandArgs.newGeoRadiusArgs().sortAscending().includeDistance().includeCoordinates();
				return connection.geoRadius(serializer.serialize(key), circle, args);
			}

		});

		return geoResults.getContent();
	}

	// ================= hashMap
	@Override
	public void hashAdd(String name, String k, Object v) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			ops.put(key, k, v);
		} catch (Throwable t) {
			log.error("缓存[" + key + "]失败, value[" + v + "]", t);
		}
	}

	@Override
	public boolean hashAddNX(String name, String k, Object v) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			return ops.putIfAbsent(key, k, v);
		} catch (Throwable t) {
			log.error("缓存[" + key + "]失败, value[" + v + "]", t);
		}
		return false;
	}

	@Override
	public Object hashGet(String name, String k) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			return ops.get(key, k);
		} catch (Throwable t) {
			log.error("获取[" + key + "]失败", t);
		}
		return null;
	}

	@Override
	public boolean hashDel(String name, String k) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			return ops.delete(key, k) == 1L;
		} catch (Throwable t) {
			log.error("获取[" + key + "]失败", t);
		}
		return false;
	}

	@Override
	public boolean hashExists(String name, String k) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			return ops.hasKey(key, k);
		} catch (Throwable t) {
			log.error("获取[" + key + "]失败", t);
		}
		return false;
	}

	@Override
	public boolean hashIncreaseBy(String name, String k, Long increase) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			return ops.increment(key, k, increase) == 1L;
		} catch (Throwable t) {
			log.error("获取[" + key + "]失败", t);
		}
		return false;
	}

	@Override
	public Set<String> hashKeys(String name) {
		String key = KEY_PREFIX_SET + name;
		try {
			HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
			return ops.keys(key);
		} catch (Throwable t) {
			log.error("获取[" + key + "]失败", t);
		}
		return null;
	}

	// ================= hashMap
}
